const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'rejected' // 失败

class MyPromise {
	// 状态字符串是要经常被使用的，定义成常量字符串方便复用及coding有提示
	status = PENDING
	// 成功后的值和失败的原因都要保存下来
	value = undefined
	reason = undefined
	// 异步处理时因不确定要调用哪个回调函数，因此需要保存成功回调和失败回调，方便异步处理完成后的回调
	successCallback = [] 
	failCallback = [] 

	constructor(executor) {
		// 考虑到程序的健壮性，需要对可能的错误进行捕获，如执行器和then的成功回调处
		try {
			// 在执行Promise类的时候，传递的执行器function会被立即执行
			executor(this.resolve, this.reject)
		} catch(e) {
			// 捕获执行器的执行异常
			this.reject(e)
		}
	}

	// resolve及reject不能定义为普通函数，普通函数的this会指向全局
	// 定义为箭头函数会使内部的this指向该类的实例对象，才能正确读取到类中定义的成员
	resolve = value => {
		if (this.status !== PENDING) return
		this.status = FULFILLED
		this.value = value
		// 处理异步时，判断成功回调是否存在，存在就调用
		while(this.successCallback.length) this.successCallback.shift()()
	}

	reject = reason => {
		if (this.status !== PENDING) return
		this.status = REJECTED
		this.reason = reason
		// 处理异步时，判断失败回调是否存在，存在就调用
		while(this.failCallback.length) this.failCallback.shift()()
	}

	// 判断Promise的执行状态，成功则调用成功的回调函数，失败就调用失败的回调函数
	// then成功回调有一个参数，表示成功之后的值；失败回调有一个参数，表示失败后的原因
	then(successCallback, failCallback) {
		// then的成功和回调函数是可选的，值会一直往后传递(值穿透），直到遇到有处理的回调函数, then()相当于then(value => value)
		successCallback = successCallback ? successCallback : value => value
		failCallback = failCallback ? failCallback : reason => { throw reason }
		// 实现then方法的链式调用，那then方法必须返回一个promise对象
		let promise2 = new MyPromise((resolve, reject) => {
			if (this.status === FULFILLED) {
				// promise2只在创建完成时才可以调用，所以可以采用setTimeout异步代码的方式来实现内部的调用
				// 同步代码处理完promise2的创建，异步代码后面执行时可以进行promise2调用的相关处理
				// 此处也必须使用箭头函数，否则this不会指向当前的promise对象
				setTimeout(() => {
					try {
						// 如何将上一个then方法的返回值传递给下个then方法
						// 判断上一个then方法的返回值 x 是普通值还是promise对象
						// 普通值：直接调用下一个promise的resolve方法
						// promise对象：
						// 根据promise对象返回的结果，成功就调用resolve传递value，失败就调用reject传递reason
						let x = successCallback(this.value)
						resolvePromise(x, resolve, reject, promise2)
					} catch(e) {
						// 对当前promise的执行异常传递给一个promise的失败回调函数
						reject(e)
					}
				}, 0)
			} else if (this.status === REJECTED) {
				// promise2只在创建完成时才可以调用，所以可以采用setTimeout异步代码的方式来实现内部的调用
				// 同步代码处理完promise2的创建，异步代码后面执行时可以进行promise2调用的相关处理
				// 此处也必须使用箭头函数，否则this不会指向当前的promise对象
				setTimeout(() => {
					try {
						// 如何将上一个then方法的返回值传递给下个then方法
						// 判断上一个then方法的返回值 x 是普通值还是promise对象
						// 普通值：直接调用下一个promise的resolve方法
						// promise对象：
						// 根据promise对象返回的结果，成功就调用resolve传递value，失败就调用reject传递reason
						let x = failCallback(this.reason)
						resolvePromise(x, resolve, reject, promise2)
					} catch(e) {
						// 对当前promise的执行异常传递给一个promise的失败回调函数
						reject(e)
					}
				}, 0)
			} else {
				// 等待
				// 同一promise的then方法可以多次调用添加多个处理函数，“同步”时来一个处理一个，异步时先用数组保存起来
				this.successCallback.push(() => {
					setTimeout(() => {
						try {
							// 如何将上一个then方法的返回值传递给下个then方法
							// 判断上一个then方法的返回值 x 是普通值还是promise对象
							// 普通值：直接调用下一个promise的resolve方法
							// promise对象：
							// 根据promise对象返回的结果，成功就调用resolve传递value，失败就调用reject传递reason
							let x = successCallback(this.value)
							resolvePromise(x, resolve, reject, promise2)
						} catch(e) {
							// 对当前promise的执行异常传递给一个promise的失败回调函数
							reject(e)
						}
					}, 0)
				})
				this.failCallback.push(() => {
					setTimeout(() => {
						try {
							// 如何将上一个then方法的返回值传递给下个then方法
							// 判断上一个then方法的返回值 x 是普通值还是promise对象
							// 普通值：直接调用下一个promise的resolve方法
							// promise对象：
							// 根据promise对象返回的结果，成功就调用resolve传递value，失败就调用reject传递reason
							let x = failCallback(this.reason)
							resolvePromise(x, resolve, reject, promise2)
						} catch(e) {
							// 对当前promise的执行异常传递给一个promise的失败回调函数
							reject(e)
						}
					}, 0)
				})
			}
		})
		return promise2
	}

	// 无论promise对象的执行结果是成功还是失败，finally里面定义的回调函数callback都会被执行一次
	// finally方法的后面可以再次调用then方法来拿到此promise对象的执行结果，因此finally要返回promise对象
	finally(callback) {
		return this.then(value => {
			// callback的返回结果可以是普通值也可以是promise对象
			// 如果是promise对象就要去执行它得到相应的结果
			return MyPromise.resolve(callback()).then(() => value)
		}, reason => {
			return MyPromise.resolve(callback()).then(() => { throw reason })
		})
	}

	// 用于处理当前promise对象的失败情况
	// catch 相当于 then 的别名，等于调用 then(undefinded, rejected)
	// 前面的then方法可以不定义失败回调，由catch方法来执行失败情况
	catch(failCallback) {
		return this.then(undefined, failCallback)
	}
	
	// 允许按异步调用的顺序得到异步代码运行的结果
	// 参数数组的顺序一定是等到结果的顺序，返回值是promise对象，可以链式调用then方法
	// 所有结果是成功的就调用成功回调，有一个是失败的就调用失败回调
	// 声明为静态方法，可以直接用类来调用，MyPromise.all()
	static all(array) {
		// 存放结果的数组，如果是普通值直接存进来，如果是promise对象就判断它的结果是成功还是失败
		let result = []
		let index = 0
		// 返回promise对象
		return new MyPromise((resolve, reject) => {
			function addData (key, value) {
				result[key] = value
				index++
				// 处理完成后调用成功回调进行结果处理
				// 中间存在异步的操作结果，当结果个数等于数组个数才可以判断所有异步操作也有返回结果
				// 将addData的方法入在里面才可以正确调用到resolve方法
				if(index === array.length) resolve(result)
			}
		    // for循环是同步方法，里面有异步方法的参与，如何处理它的结果？
			for (let i=0; i<array.length; i++) {
				let current = array[i]
				if (current instanceof MyPromise) {
					// 如果是promise对象，先去执行它，根据它的状态，得到相应的结果
					current.then(value => addData(i, value), reason => reject(reason))
				} else {
					addData(i, array[i])
				}
			}
		})
	}

	// 作用是将给定的值转换为promise对象
	// 如果给定的值是promise对象，直接原样返回此promise对象
	// 如果给定的值是普通值，就返回新创建的promise对象，并把这个值进行包裹，这样才可以在它后面调用的then方法中拿到这个值
	static resolve(value) {
		if(value instanceof MyPromise) return value
		else return new MyPromise(resolve => resolve(value))
	}
}

function resolvePromise (x, resolve, reject, promise) {
	// then 返回的promise对象不能是调用的promise对象，会引起循环调用，程序报错
	if (x === promise) return reject(new TypeError("Chaining cycle detected for promise"))
	if (x instanceof MyPromise) {
		x.then(resolve, reject)
	} else {
		resolve(x)
	}
}

module.exports = MyPromise